const fs = require('fs')
const parser = require('@babel/parser')
const path = require('path')
const traverse = require('@babel/traverse').default
const babel = require('@babel/core')

const getModuleInfo = (file) => {
	if (!file.endsWith('.js')) {
		file += '.js'
	}

	const body = fs.readFileSync(file, 'utf-8')

	const ast = parser.parse(body, {
		sourceType: 'module'
	})

	const deps = {}
	traverse(ast, {
		ImportDeclaration({ node }) {
			const dirname = path.dirname(file)
			const abspath = './' + path.join(dirname, node.source.value)
			deps[node.source.value] = abspath
		}
	})

	const { code } = babel.transformFromAst(ast, null, {
		presets: ['@babel/preset-env']
	})

	return {
		file,
		deps,
		code
	}
}

/**
 * 讲解下parseModules方法：
 * 我们首先传入主模块路径将获得的模块信息放到temp数组里。
 * 外面的循坏遍历temp数组，
 * 此时的temp数组只有主模块循环里面再获得主模块的依赖deps遍历deps，
 * 通过调用getModuleInfo将获得的依赖模块信息push到temp数组里。
 * @param {String} file
 */
function parseModules(file) {
	const entry = getModuleInfo(file)
	const temp = [entry]

	for (let i = 0; i < temp.length; i++) {
		const deps = temp[i].deps
		if (deps) {
			for (const key in deps) {
				if (deps.hasOwnProperty(key)) {
					temp.push(getModuleInfo(deps[key]))
				}
			}
		}
	}

	const depsGraph = {}
	temp.forEach((moduleInfo) => {
		depsGraph[moduleInfo.file] = {
			deps: moduleInfo.deps,
			code: moduleInfo.code
		}
	})

	return depsGraph
}

function bundle(file) {
	const depsGraph = JSON.stringify(parseModules(file))

	return `(function (graph) {
		function require(file) {
				function absRequire(relPath) {
						return require(graph[file].deps[relPath])
				}
				var exports = {}
				;(function (require,exports,code) {
						eval(code)
				})(absRequire,exports,graph[file].code)
				return exports
		}
		require('${file}')
	})(${depsGraph})`
}

const content = bundle('./src/index.js')
fs.mkdirSync('./dist')
fs.writeFileSync('./dist/bundle.js', content)
